#include "StompedReality/Core/Graphics/RenderSystem.h"
#include "StompedReality/Core/IGame.h"
#include "StompedReality/Core/Logger.h"
#include "StompedReality/Core/MessageSystem/MessageSystem.h"
#include "StompedReality/Core/MessageSystem/MessageTypes.h"
#include <algorithm>
#include <assert.h>
#include <cmath>
#include <iostream>

namespace StompedReality
{
namespace Graphics
{
RenderSystem::RenderSystem( ) :
  StompedReality::GameAccess( )
{
  this->m_MaxRenderUnits = 10;
  this->m_RenderUnitEnqueue = 0;
  this->m_RenderGroups = new RenderGroup[1000];

  this->m_ClearMask = (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  this->m_ClearColorData.r = 0;
  this->m_ClearColorData.g = 0;
  this->m_ClearColorData.b = 0;
  this->m_ClearColorData.a = 0;

  this->m_Width = 0;
  this->m_Height = 0;
}

RenderSystem::~RenderSystem( )
{
  delete[] this->m_RenderGroups;
}

void RenderSystem::SetBackgroundColor ( float r, float g, float b, float a )
{
  LogCallFunctionInfo( "RenderSystem", "SetBackgroundColor" );
  LogInfoPair( "r", r );
  LogInfoPair( "g", g );
  LogInfoPair( "b", b );
  LogInfoPair( "a", a );

  m_ClearColorData.r = r;
  m_ClearColorData.g = g;
  m_ClearColorData.b = b;
  m_ClearColorData.a = a;
}

void RenderSystem::BeginRendering( )
{
  assert ( this->m_Game != 0 );
  this->m_RenderUnitEnqueue = 0;
}

void RenderSystem::EndRendering( )
{
  glClear ( (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) );
  glClearColor ( this->m_ClearColorData.r,
                 this->m_ClearColorData.g,
                 this->m_ClearColorData.b,
                 this->m_ClearColorData.a );

  std::sort ( this->m_RenderGroups, this->m_RenderGroups + this->m_RenderUnitEnqueue,
              [&]( StompedReality::Graphics::RenderGroup a, StompedReality::Graphics::RenderGroup b )->int
              {
                return a.GetRenderOrder( ) < b.GetRenderOrder( );
              } );

  for ( int i = 0; i < this->m_RenderUnitEnqueue; ++i )
  {
    this->m_RenderGroups[i].Draw( this->m_Game );
  }
}

unsigned int RenderSystem::CalculateKey ( char layer, float objDepth )
{
  unsigned int key = 0;
  key |= layer;
  key <<= 24;
  key |= (unsigned int) (0xFFFFFF * fabs( objDepth ) * 0.5f + ((objDepth >= 0) ? 0xFFFFFF * 0.5f : 0.0f));
  return key;
}

bool RenderSystem::ParseConfig ( JsonPTR root )
{
  //assert ( this->m_RenderFunctions != 0 );
  assert ( this->m_Game != 0 );

  //this->m_RenderFunctions->SetGame ( this->m_Game );
  Json::Value rsRoot = root->get( "RenderSystem", 0 );

  // get window format
  int width = rsRoot.get ( "width", 800 ).asInt( );
  int height = rsRoot.get ( "height", 600 ).asInt( );
  bool fullscreen = rsRoot.get ( "fullscreen", 0 ).asBool( );
  bool vsync = rsRoot.get ( "vsync", 1 ).asBool( );

  LogCallFunctionInfo( "RenderSystem", "ParseConfig" );
  LogInfoPair( "width", width );
  LogInfoPair( "height", height );
  LogInfoPair( "fullscreen", fullscreen );

  if ( !this->InitWindow ( width,
                           height,
                           fullscreen,
                           rsRoot.get ( "title", "Game" ).asCString( ) ) )
  {
    LogCallFunctionError( "RenderSystem", "ParseConfig", "failed to create window" );
    return false;
  }

  // set bakground color
  if ( rsRoot.isMember ( "backgound" ) )
  {
    this->SetBackgroundColor ( ( float ) rsRoot["backgound"].get ( "r", 0 ).asDouble( ),
                               ( float ) rsRoot["backgound"].get ( "g", 0 ).asDouble( ),
                               ( float ) rsRoot["backgound"].get ( "b", 0 ).asDouble( ),
                               ( float ) rsRoot["backgound"].get ( "a", 0 ).asDouble( ) );
  }

  // Resize and init GL
  this->Resize ( width, height );
  this->InitRenderState( );
  return this->InitGLExtensions( );
}

void RenderSystem::InitRenderState( )
{
  glMatrixMode ( GL_MODELVIEW );
  glLoadIdentity( );
  glTranslatef ( 0.0f, 0.0f, 0.0f );
  glColor4f ( 1, 1, 1, 1 );
  glEnable ( GL_BLEND );

  //glEnable ( GL_DEPTH_TEST );
  //glDepthFunc ( GL_LEQUAL );
  glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

  //glBlendFunc(GL_SRC_ALPHA,GL_ONE);
  glEnableClientState ( GL_VERTEX_ARRAY );
  glEnableClientState ( GL_TEXTURE_COORD_ARRAY );
  glEnable ( GL_TEXTURE_2D );
}

void RenderSystem::Resize ( int width, int height )
{
  this->m_Width = width;
  this->m_Height = height;
  glMatrixMode ( GL_PROJECTION );
  glLoadIdentity( );
  glOrtho ( 0, this->m_Width, this->m_Height, 0, -1, 1 );
  glMatrixMode( GL_MODELVIEW );
  glViewport( 0, 0, this->m_Width, this->m_Height );
}

bool RenderSystem::InitWindow ( int width, int height, bool fullscreen, const char* title )
{
  return false;
}

bool RenderSystem::CloseWindow( )
{
  return false;
}

bool RenderSystem::IsWindowOpen( )
{
  return false;
}

bool RenderSystem::InitGLExtensions( )
{
  return false;
}

void RenderSystem::QueueRenderGroup ( RenderGroup* group )
{
  this->m_RenderGroups[this->m_RenderUnitEnqueue] = *group;
  this->m_RenderUnitEnqueue++;
}

/*const RenderAtomArray RenderSystem::GetRenderUnitList( )
{
  return this->m_RenderUnits;
}*/

int RenderSystem::GetWidth( )
{
  return this->m_Width;
}

int RenderSystem::GetHeight( )
{
  return this->m_Height;
}

void RenderSystem::SwapBuffer ( )
{
}

}
}
